//
//  smcJaikuAPI+streamPostProcess.m
//  juhu
//
//  Created by Christian Martick on 26.07.07.
//  Copyright 2007 Christian Martick. All rights reserved.
//
//	jaiku api - recent posts of all jaiku users - public timeline sort of
//
//	this objects handle the users contacts posts using rss feed
//
//	Copyright (C) 2007 Christian Martick, http://juhu-mac.blogspot.com
//
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//	THE SOFTWARE.

#import "smcJaikuAPI.h"

@implementation smcJaikuAPI (streamPostProcess)

/*	post process downloads (private methods) */

- (void) _makeIndexSetOfNewEntriesForStream: (NSString *) stream andOldArray: (NSArray*) old
{
	//step through all and look for them in old
	//if not there, add index to indexset
	
	[_indexesNew release];
	_indexesNew = [[NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange( 0, [[_streams objectForKey: stream] count])] retain];
	
	[_indexesOld release];
	_indexesOld = [[NSMutableIndexSet indexSet] retain];
	
	if (old == nil)
		return;
	
	BOOL found = NO;
	NSString *idNew;
	
	for (int i = 0; i < [[_streams objectForKey: stream] count]; i++)
	{
		found = NO;
		
		//get id of new one
		idNew = [[[_streams objectForKey: stream] objectAtIndex: i] objectForKey: kSmcEntryKeyID];
		
		//loop through old, whole one or until found
		for (int j = 0; (j < [old count]) && (!found); j++)
		{
			found = [[[old objectAtIndex: j] objectForKey: kSmcEntryKeyID] isEqualToString: idNew];
			if (found)
			{
				[_indexesNew removeIndex: i];
				[_indexesOld addIndex: j];
			}
		}
	}
}

- (void) _flickrAddURLForStream: (NSString *) stream withData: (NSData *) data andNumber: (int) index
{
	NSString *tmp = [NSString stringWithString: [self makeCleanUTF8MutableStringFromData: data]];
	if (tmp != nil)
	{
		NSDictionary *tmpDict = [NSDictionary dictionaryWithJSONString: tmp];
		if (tmpDict != nil)
		{
			//content is flickr url
			NSMutableString *flickr = [NSMutableString stringWithString: [tmpDict objectForKey: @"content"]];
			
			//replace...
			[flickr replaceOccurrencesOfString: @"\\/" withString: @"/" options: NSBackwardsSearch range: NSMakeRange( 0, [flickr length])];
			[flickr replaceOccurrencesOfString: @"_m." withString: @"_s." options: NSBackwardsSearch range: NSMakeRange( 0, [flickr length])];
			[flickr replaceOccurrencesOfString: @"_d." withString: @"_s." options: NSBackwardsSearch range: NSMakeRange( 0, [flickr length])];
			
			//create entry
			NSMutableString *tmpEntry = [NSMutableString stringWithString: 
				[[[_streams objectForKey: stream] objectAtIndex: index] objectForKey: kSmcEntryKeyText]];
		
			[tmpEntry replaceOccurrencesOfString: @"http://jaiku.com/images/icons/fav-flickr.gif\" class=\"icon\""
				withString: [NSString stringWithFormat: @"%@\" class=\"flickr\"", flickr]
				options: NSBackwardsSearch range: NSMakeRange( 0, [tmpEntry length])];
			
			//insert into dict
			[[[_streams objectForKey: stream] objectAtIndex: index] setObject: tmpEntry 
				forKey: kSmcEntryKeyText];				
		}
	}

}

- (void) _checkIfAllDoneForStream: (NSString *) stream
{
	if ((_imagesStillToCatch == 0) && (_flickrStillToCatch == 0))
	{
		_doingUpdate = NO;
		[[self delegate] stream: stream updatedWithResult: [_streams objectForKey: stream] andIndexSetForNewPosts: _indexesNew];
	}
}

- (NSString *) _jsonURLStringExtend: (NSString *) stringURL
{
	return [NSString stringWithFormat: @"%@/json", stringURL];
}

- (void) _flickrImagesForStream: (NSString *) stream
{
	//feed doesn't include flickr images, but only generic icon
	//so look at all entries and if flickr load json feed and get flickr image url
	
	_flickrStillToCatch = 0;
	
	//do all post
	for (int i=0; i< [[_streams objectForKey: stream] count]; i++)
	{
		//figure out if flickr
		if ([[[[[_streams objectForKey: stream] objectAtIndex: i] objectForKey: kSmcEntryKeyURL] absoluteString] 
			rangeOfString: @"flickr.com"].location != NSNotFound)
		{
			//is flickr post so start json download
			NSURL *url = [NSURL URLWithString: 
				[self _jsonURLStringExtend: [[[_streams objectForKey: stream] objectAtIndex: i] objectForKey: kSmcEntryKeyID]]];
			smcAsyncConnection *con = [[[smcAsyncConnection alloc] init] autorelease];
			[con setDelegate: self];
			_flickrStillToCatch++;
			[con beginDownloadFromURL: url
				withTimout: 20.0 userAgent: JUHUCLIENT_VERSION 
				andInfo: [NSDictionary dictionaryWithObjectsAndKeys: 
						@"flickrcatch", @"kind",
						stream, @"stream",
						[NSNumber numberWithInt: i], @"number", NULL]];
		}
	}
}

- (void) _loadImagesForStream: (NSString *) stream andOldArray: (NSArray *) oldArray
{
	NSMutableArray *todoEntries = [NSMutableArray arrayWithCapacity: 20];
	NSDictionary *entry = nil;
	
	for (int i = 0; i <[[_streams objectForKey: stream] count]; i++)
	{
		entry = [[_streams objectForKey: stream] objectAtIndex: i];
		
		// is the image already there?
		if ([_userImages objectForKey: [entry objectForKey: kSmcEntryKeyNick]] == nil)
		{
			// add to queue
			_imagesStillToCatch += 1;
			[todoEntries addObject: entry];
		}
	}
	
	
	// process queue
	for (int i = 0; i <[todoEntries count]; i++)
	{
		entry = [todoEntries objectAtIndex: i];
	
		smcAsyncConnection *con = [[[smcAsyncConnection alloc] init] autorelease];
		[con setDelegate: self];
		[con beginDownloadFromURL: [entry objectForKey: kSmcEntryKeyUserImageURL] 
					   withTimout: 20.0 userAgent: JUHUCLIENT_VERSION 
						  andInfo: [NSDictionary dictionaryWithObjectsAndKeys: 
									@"imagecatch", @"kind",
									stream, @"stream",
									[entry objectForKey: kSmcEntryKeyNick], @"nick", NULL]];
	}
}


- (NSString *) _addLinks: (NSString *) text
{
	//take http:// and www. and make it a link...
	//... but only if it isn't one already!
	//will only work in paragraphs before last one
	//pretty ugly, but works for now (regexp would be nice, wouldn't it?)

	NSString *aLink = nil;
	int httpLocation, httpEnd;
	
	//find last paragraph
	int lastParagraph = [text rangeOfString: @"<p>" options: NSBackwardsSearch].location;
	
	//no last paragraph? something is fundamentally wrong -> return
	if (lastParagraph == NSNotFound)
		return text;
	
	//find first www.
	httpLocation = [text rangeOfString: @"www."].location;
	NSMutableString *tmp = [NSMutableString stringWithString: text];

	//loop through all wwws
	while ((httpLocation != NSNotFound) && (httpLocation < lastParagraph))
	{
		// is it a valid link already?
		
		// most likely hack: look for <a href*</a> and if pos1<httploc<pos2 -> already a link! (not 100%!)
		int positionOpenA, positionCloseA;
		positionOpenA = [text rangeOfString: @"<a href" options: NSCaseInsensitiveSearch range: NSMakeRange(0, [tmp length] - httpLocation)].location;
		positionCloseA = [text rangeOfString: @"</a>" options: NSCaseInsensitiveSearch range: NSMakeRange(httpLocation, [tmp length] - httpLocation)].location;
		
		if (!((positionOpenA < httpLocation) && (httpLocation < positionCloseA)) || (positionOpenA == NSNotFound) || (positionCloseA == NSNotFound) )
		{
		
			//find out if it isn't http...
			//look for http://
			if (!([[tmp substringWithRange: NSMakeRange( httpLocation - 7, 7)] isEqualToString: @"http://"] || 
				 [[tmp substringWithRange: NSMakeRange( httpLocation - 2, 2)] isEqualToString: @"\">"]))
			{
				//find end of url
				//space, linebreak, paragraph
				httpEnd = [tmp rangeOfString: @" " options: 0 range: NSMakeRange( httpLocation, [tmp length] - httpLocation)].location;
				if ((httpEnd > [tmp rangeOfString: @"\n" options: 0 range: NSMakeRange( httpLocation, [tmp length] - httpLocation)].location) ||
					(httpEnd > [tmp rangeOfString: @"</p>" options: 0 range: NSMakeRange( httpLocation, [tmp length] - httpLocation)].location))
				{
					httpEnd = [tmp rangeOfString: @"\n" options: 0 range: NSMakeRange( httpLocation, [tmp length] - httpLocation)].location;
					if (httpEnd > [tmp rangeOfString: @"</p>" options: 0 range: NSMakeRange( httpLocation, [tmp length] - httpLocation)].location)
						httpEnd = [tmp rangeOfString: @"</p>" options: 0 range: NSMakeRange( httpLocation, [tmp length] - httpLocation)].location;
				}
				
				aLink = [NSString stringWithFormat: @"<a href=\"http://%@\">%@</a>",
					[tmp substringWithRange: NSMakeRange( httpLocation, httpEnd - httpLocation)],
					[tmp substringWithRange: NSMakeRange( httpLocation, httpEnd - httpLocation)]];
				
				//replace
				[tmp replaceCharactersInRange: NSMakeRange( httpLocation, httpEnd - httpLocation)
					withString: aLink];
				
				//recalc locations
				lastParagraph = [tmp rangeOfString: @"<p>" options: NSBackwardsSearch].location;
				httpLocation = httpLocation + [aLink length] - 4;
			} 
		}
		
		httpLocation = [tmp rangeOfString: @"www." options: 0  range: NSMakeRange( httpLocation + 4, [tmp length] - httpLocation - 4 )].location;

	}

	//find first http://
	httpLocation = [tmp rangeOfString: @"http://"].location;
	lastParagraph = [tmp rangeOfString: @"<p>" options: NSBackwardsSearch].location;
	
	//loop through all https
	while ((httpLocation != NSNotFound) && (httpLocation < lastParagraph))
	{
		//find out if it isn't already tag
		//looks for =" and ">
		if (!([[tmp substringWithRange: NSMakeRange( httpLocation - 2, 2)] isEqualToString: @"=\""] || 
			 [[tmp substringWithRange: NSMakeRange( httpLocation - 2, 2)] isEqualToString: @"\">"]))
		{
			//find end of url
			//space, linebreak, paragraph
			httpEnd = [tmp rangeOfString: @" " options: 0 range: NSMakeRange( httpLocation, [tmp length] - httpLocation)].location;
			if ((httpEnd > [tmp rangeOfString: @"\n" options: 0 range: NSMakeRange( httpLocation, [tmp length] - httpLocation)].location) ||
				(httpEnd > [tmp rangeOfString: @"</p>" options: 0 range: NSMakeRange( httpLocation, [tmp length] - httpLocation)].location))
			{
				httpEnd = [tmp rangeOfString: @"\n" options: 0 range: NSMakeRange( httpLocation, [tmp length] - httpLocation)].location;
				if (httpEnd > [tmp rangeOfString: @"</p>" options: 0 range: NSMakeRange( httpLocation, [tmp length] - httpLocation)].location)
					httpEnd = [tmp rangeOfString: @"</p>" options: 0 range: NSMakeRange( httpLocation, [tmp length] - httpLocation)].location;
			}
			
			aLink = [NSString stringWithFormat: @"<a href=\"%@\">%@</a>",
				[tmp substringWithRange: NSMakeRange( httpLocation, httpEnd - httpLocation)],
				[tmp substringWithRange: NSMakeRange( httpLocation, httpEnd - httpLocation)]];
			
			//replace
			[tmp replaceCharactersInRange: NSMakeRange( httpLocation, httpEnd - httpLocation)
				withString: aLink];
			
			//recalc locations
			lastParagraph = [tmp rangeOfString: @"<p>" options: NSBackwardsSearch].location;
			httpLocation = httpLocation + [aLink length] - 7;
		}
		httpLocation = [tmp rangeOfString: @"http://" options: 0  range: NSMakeRange( httpLocation + 7, [tmp length] - httpLocation - 7 )].location;
	}
	
	return tmp;
}

- (NSString *) _removeLastParagraphFrom: (NSString*) text andAddLocation: (NSString *) location
{
	// removes the las <p></p>, which is
	// basically the line with time info and so on

	NSRange range = [text rangeOfString: @"<p>" options: NSBackwardsSearch range: NSMakeRange( 0, [text length])];
	if (range.location != NSNotFound)
	{
		if ([location isEqualToString: @""])
			return [NSString stringWithString: [text substringToIndex: range.location]];
		else
			return [NSString stringWithFormat: @"%@<p class=\"footer\">from <a href=\"http://maps.google.com/maps?q=%@\">%@</a></p>", 
					[text substringToIndex: range.location], location, location];
	}
	else
		return text;
} 

- (NSString *) _addFirstParagraphTo: (NSString *) text withName: (NSString *) name andTime: (NSString *) time
{
	//adds a first <p class="title"></p> with name and relative time
	NSString *title = [NSString stringWithFormat: @"<p class=\"title\">%@ <em>%@</em></p>", name, time];
	return [NSString stringWithFormat: @"%@%@", title, text];
}

- (NSString *) _commentLastParagraph: (NSString *) text
{
	//find last paragraph and just add class to <p>
	NSRange range = [text rangeOfString: @"<p>" options: NSBackwardsSearch range: NSMakeRange( 0, [text length])];
	if (range.location != NSNotFound)
	{
		NSMutableString *result = [NSMutableString stringWithString: text];
		[result replaceCharactersInRange: range withString: @"<p class=\"footer\">"];
		return result;
	}
	else
		return text;
}

- (NSString *) _channelFooterFromURL: (NSURL *) channelURL toText: (NSString *) text andNick: (NSString *) nick
{
	//add last paragraph with channel info...
	
	//get channel...
	NSRange range = [[channelURL absoluteString] rangeOfString: @"http://jaiku.com/channel/"];
	int start = range.location + range.length;
	NSString *tmp = [NSMutableString stringWithString: [[channelURL absoluteString] substringFromIndex: start]];
	tmp = [tmp substringWithRange: NSMakeRange( 0, [tmp rangeOfString: @"/"].location)];

	//done, remove last paragraph and put it together
	range = [text rangeOfString: @"<p>" options: NSBackwardsSearch range: NSMakeRange( 0, [text length])];
	if (range.location != NSNotFound)
		return [NSString stringWithFormat: @"%@<p class=\"footer\">Post from <a href=\"http://%@.jaiku.com\">%@</a> to channel \
			<a href=\"http://jaiku.com/channel/%@\">#%@</a></p>", [text substringToIndex: range.location], nick, nick, tmp, tmp];
	else
		return [NSString stringWithFormat: @"%@<p class=\"footer\">Post from <a href=\"http://%@.jaiku.com\">%@</a> to channel \
			<a href=\"http://jaiku.com/channel/%@\">#%@</a></p>", text, nick, nick, tmp, tmp];
}

- (void) _processEntriesFor: (NSString *) stream
{
	//check if every key exists, if not, add it with some value (prevents unchecked accesses to entries by keys)
	for (int i = 0; i < [[_streams objectForKey: stream] count]; i++)
	{
		NSMutableDictionary *entry = [NSMutableDictionary dictionaryWithDictionary: [[_streams objectForKey: stream] objectAtIndex: i]];
		
		if ((![entry objectForKey: kSmcEntryKeyFirstName]) || ([[entry objectForKey: kSmcEntryKeyFirstName] isEqualToString: @""]))
			[entry setObject: [NSString stringWithString: @"Unknown"] forKey: kSmcEntryKeyFirstName];
		
		if (![entry objectForKey: kSmcEntryKeyTitle])
			[entry setObject: [NSString stringWithString: @"untitled"] forKey: kSmcEntryKeyTitle];
			
		if (![entry objectForKey: kSmcEntryKeyText])
			[entry setObject: [NSString stringWithString: @"no content given"] forKey: kSmcEntryKeyText];
		
		if (![entry objectForKey: kSmcEntryKeyURL])
			[entry setObject: [NSURL URLWithString: @"http://jaiku.com"] forKey: kSmcEntryKeyURL];
			
		if (![entry objectForKey: kSmcEntryKeyID])
			[entry setObject: [NSString stringWithString: @"?"] forKey: kSmcEntryKeyID];
			
		if (![entry objectForKey: kSmcEntryKeyDate])
			[entry setObject: [NSDate date] forKey: kSmcEntryKeyDate];

		if (![entry objectForKey: kSmcEntryKeyNick] || ([[entry objectForKey: kSmcEntryKeyNick] length] == 0))
			[entry setObject: [NSString stringWithString: @"Unknown"] forKey: kSmcEntryKeyNick];
	
		if (![entry objectForKey: kSmcEntryKeyLastName])
			[entry setObject: [NSString stringWithString: @"Unknown"] forKey: kSmcEntryKeyLastName];
			
		if (![entry objectForKey: kSmcEntryKeyLocation])
			[entry setObject: [NSString stringWithString: @""] forKey: kSmcEntryKeyLocation];
		
		if (![entry objectForKey: kSmcEntryKeyRelativeDateString])
			[entry setObject: [NSString stringWithString: @""] forKey: kSmcEntryKeyRelativeDateString];
		
		if (![entry objectForKey: kSmcEntryKeyCommentCount])
			[entry setObject: [NSNumber numberWithInt: 0] forKey: kSmcEntryKeyCommentCount];
		
		if (![entry objectForKey: kSmcEntryKeyUserURL])
			[entry setObject: [NSURL URLWithString: @"http://jaiku.com"] forKey: kSmcEntryKeyUserURL];
			
		if (![entry objectForKey: kSmcEntryKeyUserImageURL])
			[entry setObject: [NSURL URLWithString: @""] forKey: kSmcEntryKeyUserImageURL];
		
		if ([[[entry objectForKey: kSmcEntryKeyURL] absoluteString] rangeOfString: @"http://jaiku.com/channel/"].location != NSNotFound)
			[entry setObject: [NSNumber numberWithBool: YES] forKey: kSmcEntryKeyChannelFlag];
		else
			[entry setObject: [NSNumber numberWithBool: NO] forKey: kSmcEntryKeyChannelFlag];
		
		[entry setObject: [self _addLinks: [entry objectForKey: kSmcEntryKeyText]] forKey: kSmcEntryKeyText];
		
		//last paragraph handle depending on what kind of entry
		if ([[[entry objectForKey: kSmcEntryKeyURL] absoluteString] rangeOfString: @"#c-"].location != NSNotFound)
			[entry setObject: [self _commentLastParagraph:[entry objectForKey: kSmcEntryKeyText]] forKey: kSmcEntryKeyText];
		else if ([[[entry objectForKey: kSmcEntryKeyURL] absoluteString] rangeOfString: @"http://jaiku.com/channel/"].location != NSNotFound)
			[entry setObject: [self _channelFooterFromURL: [entry objectForKey: kSmcEntryKeyURL]
				toText: [entry objectForKey: kSmcEntryKeyText] 
				andNick:[entry objectForKey: kSmcEntryKeyNick]] forKey: kSmcEntryKeyText];
		else
			[entry setObject: [self _removeLastParagraphFrom:[entry objectForKey: kSmcEntryKeyText] andAddLocation: 
				[entry objectForKey: kSmcEntryKeyLocation]] forKey: kSmcEntryKeyText];
				
		[entry setObject: [self  _addFirstParagraphTo: [entry objectForKey: kSmcEntryKeyText]
			withName: [NSString stringWithFormat: @"%@ (%@) ", [entry objectForKey: kSmcEntryKeyFirstName], [entry objectForKey: kSmcEntryKeyNick]] 
			andTime: [entry objectForKey: kSmcEntryKeyRelativeDateString]] 
			forKey: kSmcEntryKeyText];
		
		[[_streams objectForKey: stream] replaceObjectAtIndex: i withObject: entry];
	}
}

- (void) _streamProcessingForStream: (NSString *) stream withData: (NSData *) theData
{
	if (theData == nil)
		{
			//failure, call delegate 
			_doingUpdate = NO;
			[[self delegate] streamUpdateFailedWithError:
				[NSError errorWithDomain: @"streamUpdate"
					code: kSmcErrorNoConnection userInfo: [NSDictionary dictionaryWithObjectsAndKeys: 
						@"Could not update stream", @"errorAbstract",
						kSmcErrorGenericUpdateFailure, @"errorText", NULL]]];
			return;
		}

	//parse it all
	
	/*
		jaiku feed error fixes
		non-utf8 chars remove and
		&_ -> &amp;
	*/
	
	//utf-8 problems...
	NSMutableString *tmp = [self makeCleanUTF8MutableStringFromData: theData];

	if (tmp == nil)
	{
		//parse error, call delegate with error
		_doingUpdate = NO;
		[[self delegate] streamUpdateFailedWithError:
			[NSError errorWithDomain: @"streamUpdate"
				code: kSmcErrorParsingError userInfo: [NSDictionary dictionaryWithObjectsAndKeys: 
					@"Could not update stream", @"errorAbstract",
					kSmcErrorGenericUpdateFailure, @"errorText", NULL]]];
	}
	
	//the other fix
	[tmp replaceOccurrencesOfString: @"& " withString: @"&amp; " options: NSBackwardsSearch range: NSMakeRange(0, [tmp length])];
	
	smcJaikuRSSParser *parser = [[smcJaikuRSSParser alloc] initWithData: [tmp dataUsingEncoding: NSUTF8StringEncoding]];
	
	if (parser)
	{
		//parse!
		if(![parser parse])
		{
			//parse error, call delegate with error
			_doingUpdate = NO;
			[[self delegate] streamUpdateFailedWithError:
				[NSError errorWithDomain: @"streamUpdate"
					code: kSmcErrorParsingError userInfo: [NSDictionary dictionaryWithObjectsAndKeys: 
						@"Could not update stream", @"errorAbstract",
						kSmcErrorGenericUpdateFailure, @"errorText", NULL]]];
			
			[parser release];
			return;
		}

		if(![parser isRSSFeed])		
		{
			//not rss feed
			_doingUpdate = NO;
			[[self delegate] streamUpdateFailedWithError:
				[NSError errorWithDomain: @"streamUpdate"
					code: kSmcErrorParsingNotRSS userInfo: [NSDictionary dictionaryWithObjectsAndKeys: 
						@"Could not update stream", @"errorAbstract",
						@"The data downloaded does seem not to be a rss feed, therefore the update failed", @"errorText", NULL]]];
			[parser release];
			return;
		}
	}
	else
	{
		//parse couldnt init
		_doingUpdate = NO;
		[[self delegate] streamUpdateFailedWithError:
			[NSError errorWithDomain: @"streamUpdate"
				code: kSmcErrorParserInitError userInfo: [NSDictionary dictionaryWithObjectsAndKeys: 
					@"Could not update stream", @"errorAbstract",
					kSmcErrorGenericUpdateFailure, @"errorText", NULL]]];
		[parser release];
		return;
	}
	
	//put entries to dictionary, post process and load images
	if ([[parser entries] count] > 0)
	{
		//save old array for finding new post
		NSArray *tmpOld = [NSArray arrayWithArray: [_streams objectForKey: stream]];
		
		[_streams setObject: [NSMutableArray arrayWithArray: [parser entries]] forKey: stream];
		[self _processEntriesFor: stream];
		
		//find new ones
		[self _makeIndexSetOfNewEntriesForStream: stream andOldArray: tmpOld];
		
		//do image downloads
		[self _loadImagesForStream: stream andOldArray: tmpOld];
		[self _flickrImagesForStream: stream];
		
		//if no flickrs or avatar images -> everything done! call delegate
		[self _checkIfAllDoneForStream: stream];
	}

	[parser release];
}

@end
